package com.alogic.xscript.ldap;

import com.alogic.xscript.ExecuteWatcher;
import com.alogic.xscript.Logiclet;
import com.alogic.xscript.LogicletContext;
import com.alogic.xscript.doc.XsObject;
import com.alogic.xscript.plugins.Segment;
import com.anysoft.util.Properties;
import com.anysoft.util.PropertiesConstants;
import org.apache.commons.lang3.StringUtils;

import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.*;

/**
 * LDAP搜索
 */
public class LDAPScan extends Segment {
    /**
     * 上下文id
     */
    protected String pid = "$ldap";
    /**
     * 缺省过滤条件
     */
    protected final static String FILTER_DEFAULT = "(objectclass=*)";

    /**
     * 条目的dn
     */
    protected String $dn;

    /**
     * 结果id
     */
    protected String id;

    /**
     * 搜索范围
     */
    protected int scope = SearchControls.ONELEVEL_SCOPE;

    /**
     * 过滤条件
     */
    protected String $filter = FILTER_DEFAULT;

    /**
     * 返回属性列表，逗号分隔
     */
    protected String $attrs = "";

    /**
     * 记录偏移位置
     */
    protected String $offset = "0";

    /**
     * 记录条数
     */
    protected String $limit = "100";

    public LDAPScan(String tag, Logiclet p) {
        super(tag, p);
    }

    @Override
    public void configure(Properties p){
        super.configure(p);
        id = PropertiesConstants.getString(p,"id","",true);
        pid = PropertiesConstants.getString(p,"pid",pid,true);
        $dn = PropertiesConstants.getRaw(p,"dn",$dn);

        $filter = PropertiesConstants.getRaw(p,"filter",$filter);
        $attrs = PropertiesConstants.getRaw(p,"attrs",$attrs);
        $offset = PropertiesConstants.getRaw(p,"offset",$offset);
        $limit = PropertiesConstants.getRaw(p,"limit",$limit);

        int scopeValue = PropertiesConstants.getInt(p,"scope",SearchControls.ONELEVEL_SCOPE,true);
        scope = (scopeValue < 0 || scopeValue > 2)?SearchControls.ONELEVEL_SCOPE:scopeValue;
    }

    @Override
    protected void onExecute(XsObject root,XsObject current,final LogicletContext ctx,final ExecuteWatcher watcher){
        DirContext dirContext = ctx.getObject(pid);
        if (dirContext == null){
            log("Can not find dir context object,check your script,pid=" + pid,LOG_ERROR,ctx);
            return ;
        }

        String dn = PropertiesConstants.transform(ctx,$dn,"");
        if (StringUtils.isEmpty(dn)){
            try {
                dn = dirContext.getNameInNamespace();
            }catch (NamingException ex){
                ex.printStackTrace();
            }
            if (StringUtils.isEmpty(dn)) {
                log("The dn is null,operation is ignored.",LOG_ERROR,ctx);
                return;
            }
        }

        NamingEnumeration result = null;
        try {
            SearchControls sc = new SearchControls();
            sc.setSearchScope(scope);
            sc.setCountLimit(PropertiesConstants.transform(ctx,$limit,100));

            String reqAttrList = PropertiesConstants.transform(ctx,$attrs,"");
            if (StringUtils.isNotEmpty(reqAttrList)){
                sc.setReturningAttributes(reqAttrList.split(","));
            }

            result = dirContext.search(dn,PropertiesConstants.transform(ctx,$filter,FILTER_DEFAULT),sc);

            if (result != null){
                while (result.hasMore()){
                    SearchResult sr = (SearchResult) result.next();

                    ctx.SetValue("$name",sr.getName());
                    ctx.SetValue("$dn",sr.getNameInNamespace());

                    StringBuffer buf = new StringBuffer("0");
                    Attributes attrs = sr.getAttributes();
                    if (attrs != null) {
                        NamingEnumeration<String> attrIds = attrs.getIDs();
                        while (attrIds.hasMore()) {
                            String attrId = attrIds.next();
                            String attrValue = attrs.get(attrId).get().toString();
                            ctx.SetValue(String.format("$attr.%s",attrId),attrValue);
                            buf.append(",").append(attrId);
                        }
                    }

                    ctx.SetValue("$attr",buf.toString());
                    super.onExecute(root,current,ctx,watcher);
                }
            }
        }catch (NamingException ex){
            log("Can not create dir context:" + ex.toString(),LOG_ERROR,ctx);
            if (StringUtils.isNotEmpty(id)){
                ctx.SetValue(id,ex.getClass().getName());
                ctx.SetValue(id + ".reason",ex.toString(true));
            }
        }finally{
            if (result != null){
                try {
                    result.close();
                }catch (Exception ex){
                    log("Something wrong to close ldap search result:" + ex.getMessage(),LOG_ERROR,ctx);
                }
            }
        }
    }
}